//
//  NSString+Network.m
//  BaseCode
//
//  Created by 曹连华 on 2019/12/4.
//  Copyright © 2019 曹连华. All rights reserved.
//

#import "NSString+Network.h"
#import "NetworkJSONUtil.h"
#import <CommonCrypto/CommonCrypto.h>

static NSString *const kAFCharactersToBeEscapedInQueryString = @":/?&=;+!@#$()',*";

static NSString *AFPercentEscapedQueryStringKeyFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
    static NSString *const kAFCharactersToLeaveUnescapedInQueryStringPairKey = @"[].";
    
    return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef) string, (__bridge CFStringRef) kAFCharactersToLeaveUnescapedInQueryStringPairKey, (__bridge CFStringRef) kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
}

static NSString *AFPercentEscapedQueryStringValueFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
    return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef) string, NULL, (__bridge CFStringRef) kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
}

@implementation NetworkQueryPair

- (id)initWithKey:(id)aKey value:(id)aValue {
    self = [super init];
    if (!self) {
        return nil;
    }
    
    self.key = aKey;
    self.value = aValue;
    
    return self;
}

- (NSString *)URLEncodedStringWithEncoding:(NSStringEncoding)aStringEncoding {
    if (!self.value || [self.value isEqual:[NSNull null]]) {
        return AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.key description], aStringEncoding);
    } else {
        return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.key description], aStringEncoding), AFPercentEscapedQueryStringValueFromStringWithEncoding([self.value description], aStringEncoding)];
    }
}

- (NSString *)queryString {
    if (!self.value || [self.value isEqual:[NSNull null]]) {
        return [self.key description].lowercaseString;
    } else {
        return [NSString stringWithFormat:@"%@=%@", [self.key description].lowercaseString, [self.value description]];
    }
}

- (NSComparisonResult)caseInsensitiveCompare:(NetworkQueryPair *)aQueryPair {
    return [self.key caseInsensitiveCompare:aQueryPair.key];
}

@end
@implementation NSString (Network)

- (NSString *)md5String {
    if (self == nil || self.length == 0) {
        return nil;
    }
    
    const char *value = [self UTF8String];
    
    unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
    CC_MD5(value, (CC_LONG) strlen(value), outputBuffer);
    
    NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    for (NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++) {
        [outputString appendFormat:@"%02x", outputBuffer[count]];
    }
    
    return outputString;
}

- (NSString *)decryptByAESKey:(NSString *)key {
    NSParameterAssert(key);
    NSData *data = [[NSData alloc] initWithBase64EncodedString:self options:NSDataBase64DecodingIgnoreUnknownCharacters];
    NSData *plainData = [self transform:kCCDecrypt
                                   data:data
                                  byKey:key];
    
    return [[NSString alloc] initWithData:plainData encoding:NSUTF8StringEncoding];
}

- (NSString *)encryptByAESKey:(NSString *)key {
    NSParameterAssert(key);
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    NSData *plainData = [self transform:kCCEncrypt
                                   data:data
                                  byKey:key];
    
    return [plainData base64EncodedStringWithOptions:(NSDataBase64EncodingOptions) 0];
}

- (NSData *)transform:(CCOperation)encryptOrDecrypt
                 data:(NSData *)inputData
                byKey:(NSString *)key {
    NSData *secretKey = [self getAESKey:key];
    
    CCCryptorRef cryptor = NULL;
    CCCryptorStatus status = kCCSuccess;
    
    uint8_t iv[kCCBlockSizeAES128 + 1];
    memset((void *) iv, 0x0, (size_t) sizeof(iv));
    
    status = CCCryptorCreate(encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding | kCCOptionECBMode,
                             [secretKey bytes], kCCKeySizeAES128, iv, &cryptor);
    
    if (status != kCCSuccess) {
        return nil;
    }
    
    size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t) [inputData length], true);
    
    void *buf = malloc(bufsize * sizeof(uint8_t));
    memset(buf, 0x0, bufsize);
    
    size_t bufused = 0;
    size_t bytesTotal = 0;
    
    status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t) [inputData length],
                             buf, bufsize, &bufused);
    
    if (status != kCCSuccess) {
        free(buf);
        CCCryptorRelease(cryptor);
        return nil;
    }
    
    bytesTotal += bufused;
    
    status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);
    
    if (status != kCCSuccess) {
        free(buf);
        CCCryptorRelease(cryptor);
        return nil;
    }
    
    bytesTotal += bufused;
    
    CCCryptorRelease(cryptor);
    
    return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
}

- (NSData *)getAESKey:(NSString *)passport {
    char key[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    
    NSData *keyData = [passport dataUsingEncoding:NSUTF8StringEncoding];
    char *keyBytes = (char *) [keyData bytes];
    
    NSInteger len = keyData.length < 16 ? keyData.length : 16;
    for (int i = 0; i < len; i++) {
        key[i] = keyBytes[i];
    }
    return [NSData dataWithBytes:key length:16];
}

#pragma mark ----------------------------------------- query
+ (NSString *)queryStringFromParameters:(NSDictionary *)aParameters
                               encoding:(NSStringEncoding)aStringEncoding {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    NSArray *queryPairs = [self queryPairsFromDictionary:aParameters];
    
    for (NetworkQueryPair *queryPair in queryPairs) {
        [mutablePairs addObject:[queryPair URLEncodedStringWithEncoding:aStringEncoding]];
    }
    
    return [mutablePairs componentsJoinedByString:@"&"];
}

+ (NSArray *)queryPairsFromDictionary:(NSDictionary *)dictionary {
    NSMutableArray *mQueryPairs = [NSMutableArray array];
    
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
    for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[sortDescriptor]]) {
        id nestedValue = [dictionary objectForKey:nestedKey];
        if (nestedValue) {
            if ([nestedValue isKindOfClass:[NSDictionary class]]) {
                NSString *jsonStr = [NetworkJSONUtil stringFromJSONObject:nestedValue options:0];
                [mQueryPairs addObject:[[NetworkQueryPair alloc] initWithKey:nestedKey value:jsonStr]];
            } else if ([nestedValue isKindOfClass:[NSArray class]]) {
                NSString *jsonStr = [NetworkJSONUtil stringFromJSONObject:nestedValue options:0];
                [mQueryPairs addObject:[[NetworkQueryPair alloc] initWithKey:nestedKey value:jsonStr]];
            } else if ([nestedValue isKindOfClass:[NSSet class]]) {
                //先转成array
                NSMutableArray *nestedArray = [NSMutableArray array];
                NSArray *sortedArray = [nestedValue sortedArrayUsingDescriptors:@[sortDescriptor]];
                for (id obj in sortedArray) {
                    [nestedArray addObject:obj];
                }
                
                NSString *jsonStr = [NetworkJSONUtil stringFromJSONObject:nestedArray options:0];
                [mQueryPairs addObject:[[NetworkQueryPair alloc] initWithKey:nestedKey value:jsonStr]];
            } else {
                [mQueryPairs addObject:[[NetworkQueryPair alloc] initWithKey:nestedKey value:nestedValue]];
            }
        }
    }
    
    return mQueryPairs;
}

@end
